`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          DEPTH                   = 7;
localparam          DATA_BW                 = 16;
localparam          RDPL_NUM                = 4;        // Read Data Pipe Line number, MUST >=1
localparam          ADDR_BW                 = $clog2(DEPTH);

reg                                         rst_n;
reg                                         clk;

reg                                         prd_pls;    // periodical pulse with longer period for patroling

// user side: orignal tpram write/read signals
reg                                         org_wen;
reg                 [ADDR_BW-1:0]           org_waddr;
reg                 [DATA_BW-1:0]           org_wdata;
reg                                         org_ren;
reg                 [ADDR_BW-1:0]           org_raddr;
wire                                        org_rvld;
wire                [DATA_BW-1:0]           org_rdata;

wire                                        necc_rvld;
wire                [DATA_BW-1:0]           necc_rdata;

// memory side: mux patrol, fix and orignal signals
wire                                        mux_wen;
wire                [ADDR_BW-1:0]           mux_waddr;
wire                [DATA_BW-1:0]           mux_wdata;
wire                                        mux_ren;
wire                [ADDR_BW-1:0]           mux_raddr;
wire                [DATA_BW-1:0]           mux_rdata;
wire                                        mux_rfix;   // detect error and request fixing, it's from ECC module
reg                                         cfg_frc_sbe;
wire                                        alm_ecc_err;
wire                                        alm_ecc_dbe;

reg                                         cfg_ptrl_ena;   // patroling could be disabled if normal running covers all memory space
reg                                         cfg_fix_ena;
wire                                        sts_ptrl_inc;
wire                                        sts_fix_inc;
wire                [ADDR_BW-1:0]           sts_fix_addr;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;
    prd_pls=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
        forever begin
            repeat(200) begin
                @(posedge clk);
            end
            prd_pls=`U_DLY 1'b1;
            @(posedge clk);
            prd_pls=`U_DLY 1'b0;
        end
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_sef_tp", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_RW
    integer waddr, raddr;
    reg [31:0]  rand_dat;

    org_wen  <=`U_DLY 1'b0;
    org_waddr <=`U_DLY 0;
    org_wdata <=`U_DLY 0;
    org_ren  <=`U_DLY 1'b0;
    org_raddr <=`U_DLY 0;

    cfg_ptrl_ena <=`U_DLY 1'b1;
    cfg_fix_ena  <=`U_DLY 1'b1;

    @(posedge rst_n);
    @(posedge clk);
    for(waddr=0; waddr<DEPTH; waddr=waddr+1) begin
        mem_wr_t(0, waddr, $urandom());
    end

    repeat(1000000) begin
        rand_dat = $urandom();
        if (rand_dat[0]==1'b1) begin
            raddr = $urandom_range(0, DEPTH-1);
            mem_rd_t(0, raddr);
        end
        if (rand_dat[1]==1'b1) begin
            waddr = $urandom_range(0, DEPTH-1);
            while(waddr==raddr) begin
                waddr = $urandom_range(0, DEPTH-1);
            end
            mem_wr_t(0, waddr, rand_dat[2+:DATA_BW]);
        end
    end
    rgrs.one_chk_done("GEN_RW is done.");
end

initial begin:FRC_ERR
    integer t;
    cfg_frc_sbe <=`U_DLY 1'b0;
    
    @(posedge rst_n);
    #10_000;

    forever begin
        @(posedge clk);
        t = $urandom_range(0, 20);
        if (t>0) begin
            cfg_frc_sbe <=`U_DLY 1'b0;

            repeat(t) begin
                @(posedge clk);
            end
        end
        cfg_frc_sbe <=`U_DLY 1'b1;
    end
end

initial begin:CHK_RDAT
    integer         i;

    @(posedge rst_n);

    forever begin
        @(posedge clk);
        if (necc_rvld==1'b1 || org_rvld==1'b1) begin
            if (necc_rvld==1'b0 || org_rvld==1'b0 || org_rdata!=necc_rdata) begin
                $error("[ERROR] ram read data error");
            end
        end
    end
end

// soft error fixing for tpram
sef_tp #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       )	// Read Data Pipe Line number, MUST >=1
) u_sef_tp ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .prd_pls                        (prd_pls                        ),	// periodical pulse with longer period for patroling

// user side: orignal tpram write/read signals
        .org_wen                        (org_wen                        ),
        .org_waddr                      (org_waddr                      ),
        .org_wdata                      (org_wdata                      ),
        .org_ren                        (org_ren                        ),
        .org_raddr                      (org_raddr                      ),
        .org_rvld                       (org_rvld                       ),
        .org_rdata                      (org_rdata                      ),

// memory side: mux patrol, fix and orignal signals
        .mux_wen                        (mux_wen                        ),
        .mux_waddr                      (mux_waddr                      ),
        .mux_wdata                      (mux_wdata                      ),
        .mux_ren                        (mux_ren                        ),
        .mux_raddr                      (mux_raddr                      ),
        .mux_rdata                      (mux_rdata                      ),
        .mux_rfix                       (mux_rfix                       ),	// detect error and request fixing, it's from ECC module

        .cfg_ptrl_ena                   (cfg_ptrl_ena                   ),	// patroling could be disabled if normal running covers all memory space
        .cfg_fix_ena                    (cfg_fix_ena                    ),
        .cfg_mem_ena                    (1'b1                           ),
        .sts_ptrl_inc                   (sts_ptrl_inc                   ),
        .sts_fix_inc                    (sts_fix_inc                    ),
        .sts_fix_addr                   (sts_fix_addr                   )
);
assign mux_rfix = alm_ecc_err & (~alm_ecc_dbe);

// two-port RAM
tpram_wrap #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       ),	// Read Data Pipe Line number
        .USER_DEF_TAG                   ("NONE"                         ),
        .ECC_MODE                       ("SEC_ONLY"                     ) 	// Single Error Correction only, not support Double Error Detection
) u_ecc_ram ( 
        .rst_wr_n                       (rst_n                          ),
        .clk_wr                         (clk                            ),
        .mem_wen                        (mux_wen                        ),
        .mem_waddr                      (mux_waddr                      ),
        .mem_wdata                      (mux_wdata                      ),

        .cfg_frc_sbe                    (cfg_frc_sbe                    ),	// support at ECC_MODE

        .rst_rd_n                       (rst_n                          ),
        .clk_rd                         (clk                            ),
        .mem_ren                        (mux_ren                        ),
        .mem_rdpl_ce                    ({RDPL_NUM{1'b1}}               ),
        .mem_raddr                      (mux_raddr                      ),
        .mem_rvld                       (mux_rvld                       ),
        .mem_rdata                      (mux_rdata                      ),

        .alm_ecc_err                    (alm_ecc_err                    ),	// support at ECC_MODE
        .alm_ecc_dbe                    (alm_ecc_dbe                    )	// support at ECC_MODE
);

// two-port RAM
tpram_wrap #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       ),	// Read Data Pipe Line number
        .USER_DEF_TAG                   ("NONE"                         ),
        .ECC_MODE                       ("NONE"                         ) 	// no ECC
) u_necc_ram ( 
        .rst_wr_n                       (rst_n                          ),
        .clk_wr                         (clk                            ),
        .mem_wen                        (org_wen                        ),
        .mem_waddr                      (org_waddr                      ),
        .mem_wdata                      (org_wdata                      ),

        .cfg_frc_sbe                    (1'b0                           ),	// support at ECC_MODE

        .rst_rd_n                       (rst_n                          ),
        .clk_rd                         (clk                            ),
        .mem_ren                        (org_ren                        ),
        .mem_rdpl_ce                    ({RDPL_NUM{1'b1}}               ),
        .mem_raddr                      (org_raddr                      ),
        .mem_rvld                       (necc_rvld                      ),
        .mem_rdata                      (necc_rdata                     ),

        .alm_ecc_err                    (                               ),	// support at ECC_MODE
        .alm_ecc_dbe                    (                               )	// support at ECC_MODE
);

task mem_wr_t; 
    input integer   gap;
    input integer   addr;
    input integer   data;
    begin
        repeat(gap) begin
            @(posedge clk);
        end
        org_wen  <=`U_DLY 1'b1;
        org_waddr <=`U_DLY addr;
        org_wdata <=`U_DLY data;

        @(posedge clk);
        org_wen  <=`U_DLY 1'b0;
    end
endtask

task mem_rd_t; 
    input integer   gap;
    input integer   addr;
    begin
        repeat(gap) begin
            @(posedge clk);
        end
        org_ren  <=`U_DLY 1'b1;
        org_raddr <=`U_DLY addr;

        @(posedge clk);
        org_ren  <=`U_DLY 1'b0;
    end
endtask

endmodule

